package fr.umlv.jee.jawp.persistence.dao;

import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

import fr.umlv.jee.jawp.persistence.object.Order;
import fr.umlv.jee.jawp.persistence.object.Product;
import fr.umlv.jee.jawp.persistence.object.Status;

public class OrderDAO implements IOrderDAO {
	private final DBCollection orderCollection;
	private final DBCollection productCollection;
	private int currentId;

	/*************************************
	 *			Constructor
	 * @throws MongoException 
	 * @throws UnknownHostException 
	 *************************************/

	public OrderDAO() throws UnknownHostException, MongoException { //TODO change to private, add a Singleton constructor
		// connect to mongoDB (ip and port number are optional)
		Mongo mongo = new Mongo();

		// get database from MongoDB,
		// if database doesn't exists, mongoDB will create it automatically
		DB db = mongo.getDB("JAWP_DB");

		// Get collection from MongoDB, database named "Orders"
		// if collection doesn't exists, mongoDB will create it automatically
		orderCollection = db.getCollection("Orders");

		// Get collection from MongoDB, database named "Orders"
		// if collection doesn't exists, mongoDB will create it automatically
		productCollection = db.getCollection("OrderedProduct");

		//orderCollection.drop();

		// query the last id
		DBCursor cursor = orderCollection.find().sort(new BasicDBObject("id", -1)).limit(1);
		// get the result
		if (cursor.size() == 0)
			currentId = 0;
		else {
			DBObject result = cursor.next();
			currentId = ((Integer) result.get("id")).intValue() + 1;
		}
	}

	/*************************************
	 *				Create
	 *************************************/

	@Override
	public Order createNewOrder(String customer) {
		String kartStatus = Status.KART.getName();

		/* check if the costumer has a kart */
		// search query
		BasicDBObject query = new BasicDBObject();
		query.put("customer", customer);
		query.put("status", kartStatus);

		// query it
		DBObject kart = orderCollection.findOne(query);

		if(kart != null)
			return getOrderById((Integer)kart.get("id"));

		/* the costumer has no kart : create one */
		// create an instance of Order
		Order order = new Order();
		order.setId(currentId);
		order.setStatus(Status.KART);
		order.setCustomer(customer);

		// create a document to store key and values
		BasicDBObject document = new BasicDBObject();
		document.put("id", currentId);
		document.put("status", kartStatus);
		document.put("customer", customer);

		// save it into collection
		orderCollection.insert(document);

		// increment current id
		currentId++;

		return order;
	}

	/*************************************
	 *				Getters
	 *************************************/

	@Override
	public List<Order> getOrders() {

		// query it
		DBCursor cursor = orderCollection.find();

		List<Order> orders = new LinkedList<Order>();
		while(cursor.hasNext()){
			DBObject object = cursor.next();
			orders.add(getOrderById((Integer)object.get("id")));
		}

		return orders;
	}

	@Override
	public List<Order> getOrdersByCustomer(String customer) {		 
		// search query
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("customer", customer);

		// query it
		DBCursor cursor = orderCollection.find(searchQuery);

		List<Order> orders = new LinkedList<Order>();
		while(cursor.hasNext()){
			DBObject object = cursor.next();
			orders.add(getOrderById((Integer)object.get("id")));
		}

		return orders;
	}

	@Override
	public List<Order> getNonKartOrdersByCustomer(String customer) {		 
		// search query
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("customer", customer);
		searchQuery.put("status", new BasicDBObject("$ne", Status.KART.getName()));

		// query it
		DBCursor cursor = orderCollection.find(searchQuery);

		List<Order> orders = new LinkedList<Order>();
		while(cursor.hasNext()){
			DBObject object = cursor.next();
			orders.add(getOrderById((Integer)object.get("id")));
		}

		return orders;
	}



	@Override
	public Order getOrderById(int id) {		 
		// search query
		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("id", id);

		// query it
		DBObject object = orderCollection.findOne(searchQuery);

		if(object == null){
			return null;
		}

		Order order = new Order();
		order.setId(id);
		order.setCustomer((String)object.get("customer"));
		order.setStatus(Status.getFromName((String)object.get("status")));

		return order;
	}

	public List<Product> getProductByOrder(Order order){
		// search query
		BasicDBObject query = new BasicDBObject();
		query.put("order", order.getId());

		// query it
		DBCursor cursor = productCollection.find(query);

		List<Product> products = new LinkedList<Product>();
		while(cursor.hasNext()){
			DBObject object = cursor.next();
			Product p = new Product();
			p.setASIN((String) object.get("product"));
			p.setQuantity((Integer)object.get("quantity"));
			products.add(p);
		}

		return products;
	}

	public long getNbProductByOrder(Order order){
		// search query
		BasicDBObject query = new BasicDBObject();
		query.put("order", order.getId());

		// query it
		return productCollection.count(query);
	}

	public long getNbOrderByCustomer(String customer){
		// search query
		BasicDBObject query = new BasicDBObject();
		query.put("customer", customer);

		// query it
		return orderCollection.count(query);
	}

	/*************************************
	 *				Setter
	 *************************************/

	public void addProduct(Order o, Product p){
		// get the order from the database
		BasicDBObject query = new BasicDBObject();
		query.put("order", o.getId());
		query.put("product", p.getASIN());

		// get the old value
		DBObject object = productCollection.findOne(query);
		if(object == null){
			query.put("quantity", 1);
			productCollection.insert(query);
		}else{
			object.put("quantity", (Integer) object.get("quantity")+1);
			productCollection.save(object);
		}
	}

	public void removeProduct(Order o, Product p){
		// get the order from the database
		BasicDBObject query = new BasicDBObject();
		query.put("order", o.getId());
		query.put("product", p.getASIN());

		// get the old value
		DBObject object = productCollection.findOne(query);
		if(object != null){
			int quantity = (Integer) object.get("quantity");

			if(quantity == 1){
				productCollection.remove(object);
			} else {
				object.put("quantity", (Integer) object.get("quantity")-1);
				productCollection.save(object);
			}
		}
	}

	@Override
	public void saveOrder(Order order){

		// save the order document
		BasicDBObject old = new BasicDBObject();
		old.put("id", order.getId());

		BasicDBObject update = new BasicDBObject();
		update.put("id", order.getId());
		update.put("status", order.getStatus().getName());
		update.put("customer", order.getCustomer());

		// save it into collection
		orderCollection.update(old, update);

		for(Entry<Product, Integer> entry : order.getProducts().entrySet()){
			// save the associated product documents
			old.clear();
			old.put("order", order.getId());
			update.clear();
			update.put("order", order.getId());
			update.put("product", entry.getKey());
			update.put("quantity", entry.getValue());

			// save it into collection
			productCollection.update(old, update);
		}
	}

	@Override
	public void saveOrders(List<Order> orders){
		for(Order order : orders)
			saveOrder(order);
	}

	public void setStatus(Order order, Status status){
		// save the order document		
		order.setStatus(status);
		saveOrder(order);
	}

	/*************************************
	 *				Deletion
	 *************************************/

	@Override
	public void deleteOrder(int id) {

		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("id", id);

		orderCollection.remove(orderCollection.findOne(searchQuery));

		// remove associated products

		searchQuery.clear();
		searchQuery.put("order", id);

		// query it
		DBCursor cursor = productCollection.find(searchQuery);

		while(cursor.hasNext()){
			productCollection.remove(cursor.next());
		}
	}

	@Override
	public void deleteOrdersByCustomer(int customer) {

		BasicDBObject searchQuery = new BasicDBObject();
		searchQuery.put("customer", customer);

		// query it
		DBCursor cursor = orderCollection.find(searchQuery);

		while(cursor.hasNext()){
			deleteOrder((Integer)cursor.next().get("id"));
		}
	}

	@Override
	public void clearDB() {
		productCollection.drop();
		orderCollection.drop();
		currentId = 0;
	}
}
